/*
 * Wazuh Send report test
 * Copyright (C) 2015, Wazuh Inc.
 * March 9, 2024.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#include "clearSendReport_test.hpp"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_synchronization_generated.h"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_synchronization_schema.h"
#include "../scanOrchestrator/clearSendReport.hpp"
#include "MockOsDataCache.hpp"
#include "MockReportDispatcher.hpp"
#include "TrampolineOsDataCache.hpp"
#include "TrampolineRemediationDataCache.hpp"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"
#include "socketClient.hpp"
#include "socketServer.hpp"

using ::testing::_;

const size_t MAX_RETRIES {10};
auto constexpr TEST_REPORTS_QUEUE_PATH {"queue/vd/reports"};
auto constexpr TEST_REPORTS_BULK_SIZE {1};

const std::string INTEGRITY_CLEAR_MSG_PACKAGES {
    R"(
            {
                "agent_info": {
                    "agent_id": "001",
                    "agent_ip": "192.168.33.20",
                    "agent_name": "focal"
                },
                "data_type": "integrity_clear",
                "data": {
                    "attributes_type": "syscollector_packages",
                    "id": 1700236640
                }
            }
        )"};

const std::string INTEGRITY_CLEAR_MSG_OS {
    R"(
            {
                "agent_info": {
                    "agent_id": "001",
                    "agent_ip": "192.168.33.20",
                    "agent_name": "focal"
                },
                "data_type": "integrity_clear",
                "data": {
                    "attributes_type": "syscollector_osinfo",
                    "id": 1700236640
                }
            }
        )"};

const std::string INTEGRITY_CLEAR_MSG_UNEXPECTED {
    R"(
            {
                "agent_info": {
                    "agent_id": "001",
                    "agent_ip": "192.168.33.20",
                    "agent_name": "focal"
                },
                "data_type": "integrity_clear",
                "data": {
                    "attributes_type": "syscollector_unexpected",
                    "id": 1700236640
                }
            }
        )"};

TEST_F(ClearSendReportTest, SendFormattedMsgPackages)
{
    // Mock report dispatcher.
    std::shared_ptr<MockReportDispatcher> reportDispatcher = std::make_shared<MockReportDispatcher>();
    // Send report instance.
    TClearSendReport<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                     MockReportDispatcher>
        sendReport(reportDispatcher);

    Os osData {.hostName = "osdata_hostname",
               .architecture = "osdata_architecture",
               .name = "osdata_name",
               .codeName = "osdata_codeName",
               .majorVersion = "osdata_majorVersion",
               .minorVersion = "osdata_minorVersion",
               .patch = "osdata_patch",
               .build = "osdata_build",
               .platform = "osdata_platform",
               .version = "osdata_version",
               .release = "osdata_release",
               .displayVersion = "osdata_displayVersion",
               .sysName = "osdata_sysName",
               .kernelVersion = "osdata_kernelVersion",
               .kernelRelease = "osdata_kernelRelease"};

    spOsDataCacheMock = std::make_shared<MockOsDataCache>();
    EXPECT_CALL(*spOsDataCacheMock, getOsData(_)).WillRepeatedly(testing::Return(osData));

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    // Mock scanContext.
    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_synchronization_SCHEMA));
    ASSERT_TRUE(parser.Parse(INTEGRITY_CLEAR_MSG_PACKAGES.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const SyscollectorSynchronization::SyncMsg*, const nlohmann::json*>
        syscollectorSync = SyscollectorSynchronization::GetSyncMsg(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorSync);
    scanContext->m_alerts["clean"] = nlohmann::json::object();

    EXPECT_CALL(*reportDispatcher, push(_)).Times(1);

    // Send report.
    EXPECT_NO_THROW(sendReport.handleRequest(scanContext));
}

TEST_F(ClearSendReportTest, SendFormattedMsgOsInfo)
{
    // Mock report dispatcher.
    std::shared_ptr<MockReportDispatcher> reportDispatcher = std::make_shared<MockReportDispatcher>();
    // Send report instance.
    TClearSendReport<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                     MockReportDispatcher>
        sendReport(reportDispatcher);

    Os osData {.hostName = "osdata_hostname",
               .architecture = "osdata_architecture",
               .name = "osdata_name",
               .codeName = "osdata_codeName",
               .majorVersion = "osdata_majorVersion",
               .minorVersion = "osdata_minorVersion",
               .patch = "osdata_patch",
               .build = "osdata_build",
               .platform = "osdata_platform",
               .version = "osdata_version",
               .release = "osdata_release",
               .displayVersion = "osdata_displayVersion",
               .sysName = "osdata_sysName",
               .kernelVersion = "osdata_kernelVersion",
               .kernelRelease = "osdata_kernelRelease"};

    spOsDataCacheMock = std::make_shared<MockOsDataCache>();
    EXPECT_CALL(*spOsDataCacheMock, getOsData(_)).WillRepeatedly(testing::Return(osData));

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    // Mock scanContext.
    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_synchronization_SCHEMA));
    ASSERT_TRUE(parser.Parse(INTEGRITY_CLEAR_MSG_OS.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const SyscollectorSynchronization::SyncMsg*, const nlohmann::json*>
        syscollectorSync = SyscollectorSynchronization::GetSyncMsg(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorSync);
    scanContext->m_alerts["clean"] = nlohmann::json::object();

    EXPECT_CALL(*reportDispatcher, push(_)).Times(1);

    // Send report.
    EXPECT_NO_THROW(sendReport.handleRequest(scanContext));
}

TEST_F(ClearSendReportTest, SendFormattedMsgUnexpected)
{
    // Mock report dispatcher.
    std::shared_ptr<MockReportDispatcher> reportDispatcher = std::make_shared<MockReportDispatcher>();
    // Send report instance.
    TClearSendReport<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                     MockReportDispatcher>
        sendReport(reportDispatcher);

    Os osData {.hostName = "osdata_hostname",
               .architecture = "osdata_architecture",
               .name = "osdata_name",
               .codeName = "osdata_codeName",
               .majorVersion = "osdata_majorVersion",
               .minorVersion = "osdata_minorVersion",
               .patch = "osdata_patch",
               .build = "osdata_build",
               .platform = "osdata_platform",
               .version = "osdata_version",
               .release = "osdata_release",
               .displayVersion = "osdata_displayVersion",
               .sysName = "osdata_sysName",
               .kernelVersion = "osdata_kernelVersion",
               .kernelRelease = "osdata_kernelRelease"};

    spOsDataCacheMock = std::make_shared<MockOsDataCache>();
    EXPECT_CALL(*spOsDataCacheMock, getOsData(_)).WillRepeatedly(testing::Return(osData));

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    // Mock scanContext.
    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_synchronization_SCHEMA));
    ASSERT_TRUE(parser.Parse(INTEGRITY_CLEAR_MSG_UNEXPECTED.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const SyscollectorSynchronization::SyncMsg*, const nlohmann::json*>
        syscollectorSync = SyscollectorSynchronization::GetSyncMsg(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorSync);
    scanContext->m_alerts["clean"] = nlohmann::json::object();

    EXPECT_CALL(*reportDispatcher, push(_)).Times(1);

    // Send report.
    EXPECT_NO_THROW(sendReport.handleRequest(scanContext));
}

TEST_F(ClearSendReportTest, SendFormattedMsgThrow)
{
    // Mock report dispatcher.
    std::shared_ptr<MockReportDispatcher> reportDispatcher = std::make_shared<MockReportDispatcher>();
    // Send report instance.
    TClearSendReport<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>,
                     MockReportDispatcher>
        sendReport(reportDispatcher);

    Os osData {.hostName = "osdata_hostname",
               .architecture = "osdata_architecture",
               .name = "osdata_name",
               .codeName = "osdata_codeName",
               .majorVersion = "osdata_majorVersion",
               .minorVersion = "osdata_minorVersion",
               .patch = "osdata_patch",
               .build = "osdata_build",
               .platform = "osdata_platform",
               .version = "osdata_version",
               .release = "osdata_release",
               .displayVersion = "osdata_displayVersion",
               .sysName = "osdata_sysName",
               .kernelVersion = "osdata_kernelVersion",
               .kernelRelease = "osdata_kernelRelease"};

    spOsDataCacheMock = std::make_shared<MockOsDataCache>();
    EXPECT_CALL(*spOsDataCacheMock, getOsData(_)).WillRepeatedly(testing::Return(osData));

    spRemediationDataCacheMock = std::make_shared<MockRemediationDataCache>();
    EXPECT_CALL(*spRemediationDataCacheMock, getRemediationData(_)).WillRepeatedly(testing::Return(Remediation {}));

    // Mock scanContext.
    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_synchronization_SCHEMA));
    ASSERT_TRUE(parser.Parse(INTEGRITY_CLEAR_MSG_UNEXPECTED.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const SyscollectorSynchronization::SyncMsg*, const nlohmann::json*>
        syscollectorSync = SyscollectorSynchronization::GetSyncMsg(reinterpret_cast<const char*>(buffer));
    auto scanContext =
        std::make_shared<TScanContext<TrampolineOsDataCache, GlobalData, TrampolineRemediationDataCache>>(
            syscollectorSync);
    scanContext->m_alerts["clean"] = nlohmann::json::object();

    // Simulate an exception in push method.
    EXPECT_CALL(*reportDispatcher, push(_)).WillOnce(testing::Throw(std::runtime_error("Error")));

    // Send report.
    EXPECT_NO_THROW(sendReport.handleRequest(scanContext));
}
